﻿using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;

using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using TestAssembly;

using Nemerle2.Compiler;

namespace Test
{
  /// <summary>
  /// Tests for CciTypeInfo
  /// </summary>
  [TestFixture]
  public class TypeTests : AssemblyTests
  {
    private mutable _progType : ITypeInfo;

    [SetUp]
    public Prepare(): void
    {
      Assert.AreNotEqual(0, CommonProject.LoadedAssemblies.First().AllTypes.Count() );
      _progType = CommonProject.LoadedAssemblies.First().AllTypes.First ( t => t.Name == "Program" );

      Assert.NotNull(_progType);
    }

    /// <summary>
    /// Test GetMembers() calls is cached
    /// </summary>
    [Test]
    public MembersCachedTest() : void
    {
      def ms1 = _progType.GetMembers();
      def ms2 = _progType.GetMembers();

      Assert.AreEqual ( ms1 , ms2 );
    }



    [Test]
    public MembersDeclaringType() : void
    {
      def m2 = _progType.GetMembers().OfType.[ITypeInfo]().First();
      def m  = _progType.GetMembers().First(mem => !(mem is ITypeInfo));

      Assert.AreEqual (_progType, m.DeclaringType);
      Assert.AreEqual (_progType, m2.DeclaringType);
      Assert.AreEqual (null,     _progType.DeclaringType);
    }

    [Test]
    public FullNameTest() : void
    {
      Assert.AreEqual( "TestAssembly.Program" , _progType.FullName );
    }

    [Test]
    public AliasTest() : void
    {
      def alias = CommonProject.LoadedAssemblies.First().AllTypes.OfType.[IAliasTypeInfo]().FirstOrDefault(t => t.Name == "GenericTypeAlias");
      Assert.NotNull(alias);

      Assert.IsTrue(alias.AliasedType.HasValue);
      //Assert.NotNull(alias.AliasedType.Value);
    }


    [Test]
    public VariantTest() : void
    {
      def varType = CommonProject.LoadedAssemblies.First().TopLevelTypes.First(t => t.Name == "Variant") :> IVariantTypeInfo;

      Assert.NotNull(varType);
      Assert.IsTrue(varType.IsVariant, "IsVariant property must be true for Variant type");

      def noneOpt = varType.GetMembers().OfType.[IVariantOptionTypeInfo]().FirstOrDefault(t => t.Name == "None");
      Assert.AreEqual(0, noneOpt.VariantOptionCode);

      def someOpt = varType.GetMembers().OfType.[IVariantOptionTypeInfo]().FirstOrDefault(t => t.Name == "Some");
      Assert.AreEqual(1, someOpt.VariantOptionCode);

      Assert.AreEqual(2, varType.VariantOptions.Count(), "Variant options for Variant type must be non empty and equal 2");

      Assert.IsTrue(varType.VariantOptions.Any(o => o : object == noneOpt),"this Variant should have None option");
      Assert.IsTrue(varType.VariantOptions.Any(o => o : object == someOpt),"this Variant should have Some option");

      Assert.NotNull(noneOpt.GetConstantObject());
      Assert.IsFalse(someOpt.GetConstantObject().HasValue);
    }

    [Test]
    public GetHashCodeTest() : void
    {
      Assert.AreEqual(_progType.GetHashCode(),_progType.GetHashCode());
    }

    [Test]
    public UnderlyingTypeTest() : void
    {
      Assert.IsFalse(_progType.UnderlyingType.HasValue);

      def aEnum = _progType.GetNestedTypes().FirstOrDefault(_.IsEnum);

      Assert.NotNull(aEnum);
      Assert.AreEqual("Int16", aEnum.UnderlyingType.Value.Name);
    }

    [Test]
    public IsObsoleteTest() : void
    {
      Assert.IsFalse(_progType.IsObsolete);
      def obsoleteClass  = CommonProject.LoadedAssemblies.First().AllTypes.Where(t => t.Name == "ObsoleteClass").FirstOrDefault();
      Assert.NotNull(obsoleteClass);
      Assert.IsTrue(obsoleteClass.IsObsolete);
    }
    
    [Test]
    public ImplementsTest() : void
    {
      def derivedClass = CommonProject.LoadedAssemblies.First().TopLevelTypes.FirstOrDefault(t => t.Name == "DerivedClass");      
      Assert.NotNull(derivedClass);
      Assert.AreEqual(0, _progType.Implements.Count());
      
      //Assert.AreEqual(5, derivedClass.Implements.Count());
    }
    
    //[Test]
    //public BaseTypesTest() : void
    //{
    //  def derivedClass = Assembly.TopLevelTypes.FirstOrDefault(t => t.Name == "DerivedClass");      
    //  Assert.NotNull(derivedClass);
    //  Assert.NotNull(derivedClass.GetDirectSuperTypes());
    //}

    //[Test]
    //public PrintGenericType() : void
    //{
    //  def ty = Assembly.Types.First(t => t.Name == "GenericClass");
    //  def a = Assembly.CustomAttributes.CustomAttributes.First(attr => attr.Name == "TestAssembly.MyAttr");
    //  //def m = ty.FindMethod("GetGenType", NBindingFlags.Public);

    //  System.Diagnostics.Debug.WriteLine(a.ToString());
    //}
  }
}
